# frozen_string_literal: true

ActiveAdmin.register RuleEntity do
  menu parent: ['Rules Engine'], priority: 2

  permit_params :type,
                :rule_id,
                :policy,
                rule_schema_attributes: %i[id rule_entity_id schema_id _destroy],
                # entities_attributes: %i[id rule_id type policy _destroy],
                comment_attributes: %i[id body _destroy]

  includes :rule, :schema

  index do
    # selectable_column
    id_column
    column 'Rule' do |entity|
      link_to entity.rule.name, admin_rule_path(entity.rule)
    end
    column 'Type', :type
    column 'Schema' do |entity|
      schema = entity.schema
      link_to schema.name, admin_schema_path(schema) if schema
    end
    column 'Status', :status
    column 'Stage', :stage
    column 'Policy', :policy
    column 'Version', :version
    column 'Create Time', :created_at
    column 'Update Time', :updated_at
    actions
  end

  filter :rule
  filter :type, filters: %i[equals contains]
  filter :status, as: :select, collection: proc { RuleEntity::Status.values }

  form remote: true do |f|
    entity_types = {
      'Rule::GeneralRule' => %w[
        RuleEntity::GeneralRule::Dsl
        RuleEntity::GeneralRule::Condition::All
        RuleEntity::GeneralRule::Condition::Any
        RuleEntity::GeneralRule::Condition::Dsl
      ],
      'Rule::DecisionTable' => %w[RuleEntity::DecisionTable],
      'Rule::RuleSet' => %w[RuleEntity::RuleSet]
    }
    allow_types = entity_types[rule_entity.rule&.type] || entity_types.values.flatten

    f.inputs do
      if rule_entity.rule && !rule_entity.rule.new_record?
        f.input :rule_id, as: :hidden
        f.input :rule, input_html: { disabled: true }
      else
        f.input :rule
      end
      f.input :type, as: :select, collection: allow_types
      f.input :policy, as: :select, collection: RuleEntity::Policy.values

      # byebug
      # f.inputs :schema, for: :schema
      f.inputs name: 'Schema', for: :rule_schema do |ef|
        # ef.input :type, as: :select, collection: (rule.type ? entity_types[rule.type] : entity_types.values.flatten)
        # ef.input :policy, as: :select, collection: RuleEntity::Policy.values
        ef.input :schema
      end
    end
    f.actions
  end

  controller do
    def new
      rule = Rule.find params.require(:rule_id)

      entity_types = {
        'Rule::GeneralRule' => %w[
          RuleEntity::GeneralRule::Dsl
          RuleEntity::GeneralRule::Condition::All
          RuleEntity::GeneralRule::Condition::Any
          RuleEntity::GeneralRule::Condition::Dsl
        ],
        'Rule::DecisionTable' => %w[RuleEntity::DecisionTable],
        'Rule::RuleSet' => %w[RuleEntity::RuleSet]
      }

      @allow_types = entity_types[rule.type]

      @rule_entity = RuleEntity.new(rule: rule, rule_schema: RuleSchema.new, type: @allow_types.first)
    end

    def create
      rule_entity = RuleEntityService.create_draft(params.require(:rule_entity).permit!)

      redirect_to admin_rule_entity_path(rule_entity)
    rescue StandardError => e
      logger.error e
      @result = { code: 1, message: e.message }

      respond_to do |format|
        format.js
        format.json { render json: @result }
      end
    end
  end

  show do
    columns do
      column do
        attributes_table do
          row 'Name' do |entity|
            link_to entity.rule.name, admin_rule_path(entity.rule)
          end
          row 'Description' do |entity|
            text_node entity.rule.description
          end
          row 'Schema' do |entity|
            link_to entity.schema.name, admin_schema_path(entity.schema) if entity.schema
          end

          row :type
          row :status
          row :stage
          row :policy
          row :version
          row :created_at
          row :updated_at
        end
      end

      if rule_entity.is_a?(RuleEntity::DecisionTable)
        column do
          panel 'Headers' do
            headers = rule_entity.decision_table_headers.ordinal_asc
            headers = headers.includes(:schema_variable, :type_method)

            table_for headers do
              column 'Ordinal' do |header|
                header.ordinal.next
              end

              column 'Column Name' do |header|
                text_node header.name
              end

              column 'Variable' do |header|
                link_to header.schema_variable.name, admin_schema_variable_path(header.schema_variable)
              end

              column 'IO Type' do |header|
                text_node header.schema_variable.io_type
              end

              column 'Operator' do |header|
                link_to header.type_method.name, admin_type_method_path(header.type_method)
              end
            end
          end
        end
      end
    end

    case rule_entity
    when RuleEntity::GeneralRule
      panel 'General Rule' do
        case rule_entity
        when RuleEntity::GeneralRule::Dsl
          panel 'Formula' do
            text_node rule_entity.formula.content
          end
        when RuleEntity::GeneralRule::Condition
          logic = if rule_entity.is_a?(RuleEntity::GeneralRule::Condition::Dsl)
                    rule_entity.formula.content
                  else
                    rule_entity.class.name.split('::').last
                  end
          panel "Conditions: #{logic}" do
            table_for rule_entity.rule_conditions do
              column :ordinal
              column :lvalue
              column :operator
              column :rvalue_type
              column :rvalue
            end
          end
        end
        panel 'Outputs' do
          table_for rule_entity.rule_outputs do
            column :name
            column :type
            column 'Value' do |output|
              value = output.is_a?(RuleOutput::Dsl) ? output.formula.content : output.value
              text_node value
            end
          end
        end
      end
    when RuleEntity::RuleSet
      panel 'Rule Set' do
        table_for rule_entity.rule_entities do
          column 'Rule Entity' do |sub_entity|
            link_to sub_entity.id, admin_rule_entity_path(sub_entity)
          end
          column 'Rule' do |sub_entity|
            link_to sub_entity.rule.name, admin_rule_path(sub_entity.rule)
          end
          column 'Rule Type', :type
          column 'Status', :status
          column 'Description', :description
          column 'stage', :stage
          column 'policy', :policy
          column 'Version', :version
          column 'Create Time', :created_at
          column 'Update Time', :updated_at
          actions
        end
      end
    when RuleEntity::DecisionTable
      panel 'Decision Table' do
        rows = rule_entity.decision_table_rows.ordinal_asc.page(params[:page]).per(10)

        headers = rule_entity.decision_table_headers.joins(:schema_variable)
          .select('ordinal', 'name', 'schema_variables.io_type')

        paginated_collection(rows, download_links: false) do
          cells = DecisionTableService.build_cells_from_rows(rule_entity.decision_table_headers, rows)

          table_for cells, sortable: false do
            headers.each do |header|
              column header.name, class: "dt-header-#{header.io_type}" do |row_data|
                text_node row_data["column_#{header.ordinal}"]
              end
            end
          end
        end
      end
    end

    if rule_entity.schema
      panel 'Fire a rule' do
        form_for :rule_entity, url: fire_rule_entity_path(rule_entity), remote: true do |f|
          table_for rule_entity.schema.input_variables do
            column :name
            column 'Input Value' do |variable|
              f.input name: "inputs[#{variable.name}]"
            end
          end

          hr

          div do
            f.submit 'Fire a Rule'
          end
        end

        panel 'Result', class: 'fire_result' do
        end
      end
    end
  end
end
